/*******************************************************************************
 * Copyright 2011 See AUTHORS file.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package org.xxforest.game.core.scenes.scene2d;

import java.util.List;

import org.xxforest.game.core.XX;
import org.xxforest.game.core.assets.XXAssetManager;
import org.xxforest.game.core.common.XXDisposable;
import org.xxforest.game.core.scenes.scene2d.events.XXMotionHandler;
import org.xxforest.game.core.scenes.scene2d.events.type.XXScrolledEvent;
import org.xxforest.game.core.scenes.scene2d.selector.XXSchedule;
import org.xxforest.game.core.scenes.scene2d.selector.XXSchedule.SelectorContext;
import org.xxforest.game.core.scenes.scene2d.selector.XXScheduleSelector;
import org.xxforest.game.core.scenes.scene2d.utils.XXScissorStack;
import org.xxforest.game.core.tween.XXBaseTween;

import aurelienribon.tweenengine.TweenManager;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.Camera;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;

public abstract class XXScreen extends XXNode implements  InputProcessor , XXDisposable {
	
	protected XXSchedule schedule ;
	protected TweenManager tweenManager ;
	
	private float gutterWidth, gutterHeight;
	private Camera camera;
	private SpriteBatch batch;
	private final Vector2 screenCoords = new Vector2();
	private int mouseScreenX, mouseScreenY ;
	private float mouseX ,mouseY; 
	private XXMotionHandler motionHandler ;

	public void setViewport (float width, float height, boolean keepAspectRatio) {
		if (keepAspectRatio) {
			float screenWidth = Gdx.graphics.getWidth();
			float screenHeight = Gdx.graphics.getHeight();
			if (screenHeight / screenWidth < height / width) {
				float toScreenSpace = screenHeight / height;
				float toViewportSpace = height / screenHeight;
				float deviceWidth = width * toScreenSpace;
				float lengthen = (screenWidth - deviceWidth) * toViewportSpace;
				setWidth(width + lengthen);
				setHeight(height);
				gutterWidth = lengthen / 2;
				gutterHeight = 0;
			} else {
				float toScreenSpace = screenWidth / width;
				float toViewportSpace = width / screenWidth;
				float deviceHeight = height * toScreenSpace;
				float lengthen = (screenHeight - deviceHeight) * toViewportSpace;
				setHeight(height + lengthen);
				setWidth( width);
				gutterWidth = 0;
				gutterHeight = lengthen / 2;
			}
		} else {
			setWidth( width);
			setHeight(height);
			gutterWidth = 0;
			gutterHeight = 0;
		}

		setAnchorPoint(getWidth()/2, getHeight()/2);

		camera.position.set(getAnchorPointX(), getAnchorPointY(), 0);
		camera.viewportWidth = getWidth();
		camera.viewportHeight = getHeight();
	}

	public void act () {
		act(Math.min(Gdx.graphics.getDeltaTime(), 1 / 30f));
	}

	public void act (float delta) {
	}

	public boolean touchDown (int screenX, int screenY, int pointer, int button) {
		
		screenToStageCoordinates(screenCoords.set(screenX, screenY));
		motionHandler.touchDown(screenX,screenY,screenCoords.x, screenCoords.y,pointer,button) ;
		
//		pointerTouchs[pointer] = XXPointerTouch.create();
//
//		XXNode node = findHiter(screenCoords.x, screenCoords.y);
//		if(node == null){
//			node = this ;
//		}
//		pointerTouchs[pointer].touch(this,node,screenCoords.x, screenCoords.y,pointer,button);
//		
//		boolean handled = false ;
//		if(node.hasEventListener(XXEventType.TOUCHDOWN)){
//			XXTouchEvent event = XXTouchEvent.create() ;
//			event.touchDown(node, screenCoords.x, screenCoords.y);
//			handled |= node.fire(event);
//			event.dispose() ;
//		}
		
		return true;
		
	}

	public boolean touchDragged (int screenX, int screenY, int pointer) {
		
		screenToStageCoordinates(screenCoords.set(screenX, screenY));
		motionHandler.touchDragged(screenX,screenY,screenCoords.x, screenCoords.y,pointer) ;
		
//		screenToStageCoordinates(screenCoords.set(screenX, screenY));
//		XXNode node = pointerTouchs[pointer].getFocusNode();
//		pointerTouchs[pointer].drag(screenCoords.x, screenCoords.y,pointer);
//		touchDraggedEvent(screenCoords.x, screenCoords.y,pointer);
//		
//		boolean handled = false ;
//		if(node.hasEventListener(XXEventType.TOUCHDRAGGED)){
//			XXTouchDragEvent event = XXTouchDragEvent.create() ;
//			event.touchDrag(node, pointerTouchs[pointer]);
//			handled |= node.fire(event);
//			event.dispose() ;
//		}
		return true;
	}
	
	public boolean touchUp (int screenX, int screenY, int pointer, int button) {
		
		screenToStageCoordinates(screenCoords.set(screenX, screenY));
		motionHandler.touchUp(screenX,screenY,screenCoords.x, screenCoords.y,pointer,button) ;
		
//		screenToStageCoordinates(screenCoords.set(screenX, screenY));
//		XXNode node = pointerTouchs[pointer].getFocusNode();
//		pointerTouchs[pointer].touchUp(screenCoords.x, screenCoords.y,pointer);
//		
//		boolean handled = false ;
//		if(node.hasEventListener(XXEventType.TOUCHUP)){
//			XXTouchEvent event = XXTouchEvent.create() ;
//			event.touchUp(node, screenCoords.x, screenCoords.y);
//			handled |= node.fire(event);
//			event.dispose() ;
//		}
//		pointerTouchs[pointer].dispose() ;
		
		return true;
	}

	public boolean mouseMoved (int screenX, int screenY) {
		mouseScreenX = screenX;
		mouseScreenY = screenY;
		screenToStageCoordinates(screenCoords.set(screenX, screenY));
		mouseX = screenCoords.x ;
		mouseY = screenCoords.y ;
		mouseMovedEvent(mouseX, mouseY);
		
//		Array<XXnode> targets = new Array<XXnode>() ;
//		findHiter(targets ,stageCoords.x, stageCoords.y, XXEventType.MOUSEMOVE);
//		targets.add(this);
//		
//		boolean handled = false ;
//		for(XXnode node : targets ){
//			if(node.hasEventListener(XXEventType.MOUSEMOVE)){
//				XXMouseMoveEvent event = XXMouseMoveEvent.create() ;
//				event.mouseMove(node,stageCoords.x, stageCoords.y);
//				handled |= node.fire(event);
//				event.dispose() ;
//			}
//		}
		return true;
	}
	
	/**
	 * 鼠标移动事件,必须重写才有效果
	 * @param screenCoordsX
	 * @param screenCoordsY
	 * @param pointer
	 */
	public void mouseMovedEvent(float screenCoordsX, float screenCoordsY){}

	public boolean scrolled (int amount) {
		XXNode target = XX.director.getFocus();
		if(target != null){
			screenToStageCoordinates(screenCoords.set(mouseScreenX, mouseScreenY));
			
			XXScrolledEvent event = XXScrolledEvent.create();
			event.scroll(target,amount).point(screenCoords.x,screenCoords.y).screen(this);
			boolean handled = target.fire(event); 
			event.dispose() ;
			return handled;
		}
		return false ;
	}

	public boolean keyDown (int keyCode) {
		return motionHandler.keyDown(keyCode) ;
	}

	public boolean keyUp (int keyCode) {
		return motionHandler.keyUp(keyCode);
	}

	public boolean keyTyped (char character) {
		return motionHandler.keyTyped(character);
	}

	public void clear () {
		this.clearChildren();
	}

	public float getGutterWidth () {
		return gutterWidth;
	}

	public float getGutterHeight () {
		return gutterHeight;
	}

	public SpriteBatch getSpriteBatch () {
		return batch;
	}

	public Camera getCamera () {
		return camera;
	}

	public void setCamera (Camera camera) {
		this.camera = camera;
	}


	public Vector2 screenToStageCoordinates (Vector2 screenCoords) {
		camera.unproject(Vector3.tmp.set(screenCoords.x, screenCoords.y, 0));
		screenCoords.x = Vector3.tmp.x;
		screenCoords.y = Vector3.tmp.y;
		return screenCoords;
	}

	public Vector2 stageToScreenCoordinates (Vector2 stageCoords) {
		Vector3.tmp.set(stageCoords.x, stageCoords.y, 0);
		camera.project(Vector3.tmp);
		stageCoords.x = Vector3.tmp.x;
		stageCoords.y = Vector3.tmp.y;
		return stageCoords;
	}

	public Vector2 toScreenCoordinates (Vector2 coords, Matrix4 transformMatrix) {
		XXScissorStack.toWindowCoordinates(camera, transformMatrix, coords);
		return coords;
	}

	public void dispose () {
		batch.dispose();
		tweenManager.pause() ;
		tweenManager = null ;
		
	}

	public int getMouseScreenX() {
		return mouseScreenX;
	}

	public int getMouseScreenY() {
		return mouseScreenY;
	}

	@Override
	public void draw(SpriteBatch batch ,float delta) {
		
	}

	public float getMouseX() {
		return mouseX;
	}

	public float getMouseY() {
		return mouseY;
	}
	
	/**
	 * 准备方法，用于预加载资源，在onCreate之前
	 */
	public void loadResources(XXAssetManager assetManager){
	}
	
	/**
	 * 创建方法
	 * @param assetManager
	 */
	public abstract void onCreate() ;
	
	public abstract void pause();
	
	public abstract void resume();
	
	public void render(float delta){
		tweenManager.update(delta);
		schedule.act(delta);
		motionHandler.act(delta);
		
		camera.update();
		batch.setProjectionMatrix(camera.combined);
		batch.begin();
		this.render(batch, delta);
		List<XXNode> children = getChildren();
		for(XXNode node : children){
			node.render(batch,delta);
		}
		batch.end();
	}
	
	public void initialize(){
		this.setWidth(XX.data.getWidth()) ;
		this.setHeight(XX.data.getHeight());
		this.batch = new SpriteBatch();
		camera = new OrthographicCamera();
		setViewport(getWidth() , getHeight(), false);
		
		schedule = XXSchedule.create() ;
		tweenManager = new TweenManager() ;
		motionHandler = XXMotionHandler.create(this);
		
		XX.gdx.setInputProcessor(this);
	}
	
	public SelectorContext addSelector(XXScheduleSelector selector, float sec) {
		return schedule.addSelector(selector, sec);
	}
	
	public SelectorContext addSelector(XXScheduleSelector selector) {
		return schedule.addSelector(selector, 0);
	}
	
	public void removeSelector(SelectorContext selectorContext) {
		schedule.removeSelector(selectorContext);
	}
	
	@Override
	public <T extends XXNode> XXScreen addTween(XXBaseTween<?> tween) {
		tween.start(tweenManager) ;
		return this ;
	}

	public abstract void show();

	public abstract void hide() ;

}
